﻿using System;
using System.Linq;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;

namespace Client.Hotfix
{
    public static partial class ProtocolHelper
    {
        private const int NONCE_SIZE = 12; // RFC 8439: 96 bits
        private const int MAC_SIZE = 128;  // bits

        // 固定密钥（建议从配置中读入）这里按十六进制简单取字节数组
        private static readonly byte[] KEY = HexToBytes("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
        // 辅助认证数据，增加加密的难度，可以为null,跟客户端约定即可。
        private static readonly byte[] AAD = System.Text.Encoding.UTF8.GetBytes("cowboy");

        private static readonly RandomNumberGenerator random = RandomNumberGenerator.Create();

        /// <summary>
        /// chacha20-poly1305 加密算法
        /// </summary>
        /// <param name="originalData">原始数据</param>
        /// <returns>加密后的数组 包含固定长度的nonce以及密文</returns>
        public static byte[] Encrypt(byte[] originalData)
        {
            // 生成随机 nonce，确保每次加密结果不同（即使明文一样），防止重放攻击。
            byte[] nonce = new byte[NONCE_SIZE];
            random.GetBytes(nonce);

            ChaCha20Poly1305 cipher = new ChaCha20Poly1305();
            AeadParameters parameters = new AeadParameters(new KeyParameter(KEY), MAC_SIZE, nonce, AAD);
            cipher.Init(true, parameters);

            byte[] ciphertext = new byte[cipher.GetOutputSize(originalData.Length)];
            int len = cipher.ProcessBytes(originalData, 0, originalData.Length, ciphertext, 0);
            cipher.DoFinal(ciphertext, len);

            // 拼接 nonce + ciphertext（加密后的数据）一起返回，nonce是固定长度的，ciphertext是可变长度的
            byte[] output = new byte[NONCE_SIZE + ciphertext.Length];
            Array.Copy(nonce, 0, output, 0, NONCE_SIZE);
            Array.Copy(ciphertext, 0, output, NONCE_SIZE, ciphertext.Length);
            return output;
        }

        /// <summary>
        /// chacha20-poly1305 解密算法
        /// </summary>
        /// <param name="input">加密后的数组，包含固定长度的nonce以及密文</param>
        /// <returns>解密后的数据</returns>
        public static byte[] Decrypt(byte[] input)
        {
            byte[] nonce = new byte[NONCE_SIZE];
            Array.Copy(input, 0, nonce, 0, NONCE_SIZE);
            byte[] ciphertext = new byte[input.Length - NONCE_SIZE];
            Array.Copy(input, NONCE_SIZE, ciphertext, 0, ciphertext.Length);

            ChaCha20Poly1305 cipher = new ChaCha20Poly1305();
            AeadParameters parameters = new AeadParameters(new KeyParameter(KEY), MAC_SIZE, nonce, AAD);
            cipher.Init(false, parameters);

            byte[] plaintext = new byte[cipher.GetOutputSize(ciphertext.Length)];
            int len = cipher.ProcessBytes(ciphertext, 0, ciphertext.Length, plaintext, 0);
            cipher.DoFinal(plaintext, len);

            return plaintext;
        }

        public static string ByteArrayToHex(byte[] bytes)
        {
            return BitConverter.ToString(bytes).Replace("-", " ");
        }

        private static byte[] HexToBytes(string hex)
        {
            return Enumerable.Range(0, hex.Length)
                             .Where(x => x % 2 == 0)
                             .Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
                             .ToArray();
        }
    }
}